package com.wanjia.bpsapi.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.cron.CronException;
import com.wanjia.bpsapi.exception.TaskCreateException;
import com.wanjia.bpsapi.properties.CronTaskProperties;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronExpression;

import java.lang.reflect.Method;
import java.util.List;

import static com.wanjia.bpsapi.constant.Constant.*;

@Configuration
@Slf4j
public class TaskExecutorConfig {

    @Setter(onMethod_ = @Autowired)
    private CronTaskProperties properties;

    @Setter(onMethod_ = @Autowired)
    private ApplicationContext context;

    @Bean
    public ScheduledTaskRegistrar registrar() {
        ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar();
        List<CronTaskProperties.CronTask> tasks = properties.getTasks();
        if (BooleanUtil.isTrue(properties.getEnable()) && CollUtil.isNotEmpty(tasks)) {
            tasks.stream()
                    .filter(task -> BooleanUtil.isTrue(task.getEnable()))
                    .forEach(task -> {
                        String cron = task.getCron();
                        try {
                            if (!CronExpression.isValidExpression(cron)) {
                                throw new CronException(cron);
                            }
                            registrar.addCronTask(getRunnable(task), cron);
                            log.info("定时任务注册成功[{}]", task);
                        } catch (Exception e) {
                            log.error(TASK_CREATE_ERROR, e);
                        }
                    });
        }
        return registrar;
    }

    private Runnable getRunnable(CronTaskProperties.CronTask task) {
        try {
            Class<?> target = task.getTarget();
            String methodName = task.getMethod();
            Method method;
            switch (task.getType()) {
                case BEAN:
                    method = target.getMethod(methodName);
                    Object bean = context.getBean(target);
                    return () -> {
                        try {
                            method.invoke(bean);
                        } catch (Exception e) {
                            log.error(METHOD_INVOKE_ERROR, e);
                            Thread.currentThread().interrupt();
                        }
                    };
                case RUNNABLE:
                    Class<? extends Runnable> runnable = task.getRunnable();
                    return runnable.getConstructor().newInstance();
                case STATIC:
                    method = target.getMethod(methodName);
                    return () -> {
                        try {
                            method.invoke(target);
                        } catch (Exception e) {
                            log.error(METHOD_INVOKE_ERROR, e);
                            Thread.currentThread().interrupt();
                        }
                    };
                default:
                    throw new TaskCreateException(UNKNOWN_TASK_TYPE);
            }
        } catch (Exception e) {
            throw new TaskCreateException(TASK_CREATE_ERROR, e);
        }
    }
}
